package security;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.PBEParameterSpec;
import javax.swing.KeyStroke;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class SymmetricKeyEncryptor implements KeyEncryptor {

	private PBEParameterSpec pbeParamSpec;
	private static SymmetricKeyEncryptor instance;
	private static KeyStore ks;
	private static final String keyStorePath = "chat_3";
	private static final char[] keyStorePassword = "123456".toCharArray();

	// Salt
	private byte[] salt = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
			(byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };

	// Iteration count
	private int count = 1000;

	public byte[] getSalt() {
		return salt;
	}

	public int getCount() {
		return count;
	}

	public static SymmetricKeyEncryptor getInstance() {
		if (instance == null)
			instance = new SymmetricKeyEncryptor();
		return instance;

	}

	private SymmetricKeyEncryptor() {
		Security.addProvider(new BouncyCastleProvider());
		pbeParamSpec = new PBEParameterSpec(salt, count);
		try {
			ks = KeyStore.getInstance("JCEKS");
			// get user password and file input stream
			FileInputStream fis = new FileInputStream(keyStorePath);
			ks.load(fis, keyStorePassword);
			fis.close();
		} catch (KeyStoreException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (CertificateException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	@Override
	public byte[] encrypt(byte[] data, Key key) {
		Cipher cipher;
		try {
			cipher = Cipher.getInstance("PBEWithMD5AndDES");
			cipher.init(Cipher.ENCRYPT_MODE, key, pbeParamSpec);
			// byte[] cipherText = new byte[cipher.getOutputSize(data.length)];
			// int ctLength = cipher.update(data, 0, data.length, cipherText,
			// 0);
			byte[] cipherText = cipher.doFinal(data);
			// byte[] results = new byte[ctLength];
			// System.arraycopy(cipherText, 0, results, 0, ctLength);
			return cipherText;
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		} catch (InvalidAlgorithmParameterException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	public byte[] decrypt(byte[] msg, Key key) {
		Cipher cipher;
		try {
			cipher = Cipher.getInstance("PBEWithMD5AndDES");
			cipher.init(Cipher.DECRYPT_MODE, key, pbeParamSpec);
			byte[] plainText = new byte[cipher.getOutputSize(msg.length)];
			// int ptLength = cipher.update(msg, 0, msg.length, plainText, 0);
			plainText = cipher.doFinal(msg);
			return plainText;
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		} catch (InvalidAlgorithmParameterException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	public SecretKey loadSymmetricKey(String username, String password) {
		try {
			KeyStore.PasswordProtection pt = new KeyStore.PasswordProtection(
					password.toCharArray());
			KeyStore.SecretKeyEntry secEntry;
			secEntry = (KeyStore.SecretKeyEntry) ks.getEntry(username, pt);
			SecretKey secKey = secEntry.getSecretKey();
			java.io.FileOutputStream fos = new java.io.FileOutputStream(keyStorePath);
			ks.store(fos, keyStorePassword);
			fos.close();
			return secKey;
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnrecoverableEntryException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (KeyStoreException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (CertificateException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	public void saveSymmetricKey(String username, String password, SecretKey key) {
		try {
			KeyStore.PasswordProtection pt = new KeyStore.PasswordProtection(password.toCharArray());
			// save my secret key
			KeyStore.SecretKeyEntry skEntry = new KeyStore.SecretKeyEntry(key);
			ks.setEntry(username, skEntry, pt);
		} catch (KeyStoreException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
